import java.io.BufferedWriter;
import java.io.FileWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Random;

import org.omg.CORBA.PUBLIC_MEMBER;

public class UTM extends TopicModel {

	public HashMap<Integer, Integer> wordMap = new HashMap<Integer, Integer>();;
	public HashMap<Integer, Integer> wordMapInv = new HashMap<Integer, Integer>();;

	public HashMap<Integer, Integer> auMap = new HashMap<Integer, Integer>();;
	public HashMap<Integer, Integer> auMapInv = new HashMap<Integer, Integer>();;

	public HashMap<Integer, Integer> usMap = new HashMap<Integer, Integer>();;
	public HashMap<Integer, Integer> usMapInv = new HashMap<Integer, Integer>();;

	public HashMap<Integer, Integer> mMap = new HashMap<Integer, Integer>();;
	public HashMap<Integer, Integer> mMapInv = new HashMap<Integer, Integer>();;

	public HashMap<Integer, Integer> rMap = new HashMap<Integer, Integer>();;
	public HashMap<Integer, Integer> rMapInv = new HashMap<Integer, Integer>();;

	public Integer[] docsId;
	public Integer[] groupsId;
	public Integer[] usersId;

	public int[][] docs;
	public int[][] docAuthors;
	public int[][] groupUsers;
	public int[][] userMovies;
	public int[][] umRating;
	public int[][] docsA;
	public int[][] docsZ;
	public int[][] docsZA;

	public int[][][] nAZW;
	public int[][] nAZ;
	public int[][] naAZ;
	public int[] nA;
	public int[][] nDZ;
	public int[] nD;

	public double[][] npAZ;
	public double[][] npDZ;
	public double[][] sAD;

	public int D;
	public int W;
	public int A;
	public int Z;
	public int G;
	public int U;

	public double beta;
	public double alpha;

	public UTM(int z, double a, double b) {
		alpha = a;
		beta = b;
		Z = z;
	}

	@Override
	public void initialize() {
		System.out.println("Initializing...");
		Random r = new Random();

		docsZ = new int[D][];
		docsA = new int[D][];

		nAZW = new int[A][Z][W];
		nAZ = new int[A][Z];
		naAZ = new int[A][Z];
		nA = new int[A];
		nDZ = new int[D][Z];
		nD = new int[D];
		npAZ = new double[A][Z]; // topic 锟斤拷 user锟较的分诧拷
		npDZ = new double[D][Z];// topic zai movie 锟较的分诧拷

		for (int d = 0; d < D; d++) {
			docsZ[d] = new int[docs[d].length];
			docsA[d] = new int[docs[d].length];

			for (int n = 0; n < docs[d].length; n++) {
				int w = docs[d][n];

				int a = r.nextInt(docAuthors[d].length); // select random a
															// value in
															// {0...A-1}
				int author = docAuthors[d][a];
				docsA[d][n] = author;

				int z = r.nextInt(this.Z); // select random z value in {0...Z-1}
				docsZ[d][n] = z;

				// update counts
				nA[author] += 1;
				naAZ[author][z] += 1;
				nAZ[author][z] += 1;
				nAZW[author][z][w] += 1;
				nDZ[d][z] += 1;
				nD[d] += 1;
			}
		}
	}

	@Override
	public void doSampling() {
		for (int d = 0; d < D; d++) {
			for (int n = 0; n < docs[d].length; n++) {
				int w = docs[d][n];
				int topic = docsZ[d][n];
				int author = docsA[d][n];

				// decrement counts
				nA[author] -= 1;
				naAZ[author][topic] -= 1;
				nAZ[author][topic] -= 1;
				nAZW[author][topic][w] -= 1;
				nDZ[d][topic] -= 1;
				nD[d] -= 1;

				double betaNorm = W * beta;

				// sample new value for author

				double pTotal = 0.0;
				double[] p = new double[A];

				for (int a = 0; a < this.docAuthors[d].length; a++) {
					author = this.docAuthors[d][a];
					p[a] = (naAZ[author][topic] + alpha);

					pTotal += p[a];
				}

				Random r = new Random();
				double u = r.nextDouble() * pTotal;

				double v = 0;
				for (int a = 0; a < A; a++) {
					v += p[a];

					if (v > u) {
						author = this.docAuthors[d][a];
						break;
					}
				}
				// sample new value for topic

				pTotal = 0.0;
				p = new double[Z];

				for (int z = 0; z < Z; z++) {
					p[z] = (nAZW[author][z][w] + beta)
							/ (nAZ[author][z] + betaNorm)
							* (naAZ[author][z] + alpha);

					pTotal += p[z];
				}

				r = new Random();
				u = r.nextDouble() * pTotal;

				double m = 0;
				for (int z = 0; z < Z; z++) {
					m += p[z];

					if (m > u) {
						topic = z;
						break;
					}
				}
				// increment counts

				nA[author] += 1;
				naAZ[author][topic] += 1;
				nAZ[author][topic] += 1;
				nAZW[author][topic][w] += 1;
				nDZ[d][topic] += 1;
				nD[d] += 1;

				// set new assignments

				docsZ[d][n] = topic;
				docsA[d][n] = author;

			}
		}
		likelihood();
	}

	public void npAZforRecommendation() {

		for (int a = 0; a < A; a++) {
			for (int z = 0; z < Z; z++) {
				npAZ[a][z] = naAZ[a][z] / nA[a]; // distributions of topics over
													// users
			}
		}

		for (int d = 0; d < D; d++) {
			for (int z = 0; z < Z; z++) {
				npDZ[d][z] = nDZ[d][z] / nD[d]; // distributions of topics over
												// documents
			}

		}
    // similarity of users and movies
		for (int a = 0; a < A; a++) {
			for (int d = 0; d < D; d++) {
				sAD[a][d] = 0;
				double pat = 0;
				double pdt = 0;
				for (int z = 0; z < Z; z++) {
					sAD[a][d] = sAD[a][d] + naAZ[a][z] * npDZ[d][z];
					pat = pat + naAZ[a][z] * naAZ[a][z];
					pdt = pdt + npDZ[d][z] * npDZ[d][z];
				}
				pat = Math.sqrt(pat);
				pdt = Math.sqrt(pdt);
				sAD[a][d] = sAD[a][d] / (pat * pdt);
			}
		}
          // random walk （随机初始化）
		double[][] gmrating = new double[G][G];

		Random r = new Random();
		for (int i = 0; i < G; i++) {
			for (int j = 0; j < G; j++) {
				gmrating[i][j] = r.nextDouble();
			}
		}

		

	}

	public void RwrforRecommendation() {
		npAZforRecommendation();

	}

	public void writeOutput(String filename) throws Exception {
		FileWriter fw = new FileWriter("AT.assign");
		BufferedWriter bw = new BufferedWriter(fw);

		for (int d = 0; d < D; d++) {
			int docId = docsId[d];
			for (int n = 0; n < docs[d].length; n++) {
				int word = wordMapInv.get(docs[d][n]);
				int auid = auMapInv.get(docsA[d][n]);
				bw.write(docId + "\t" + word + "\t" + auid);
				bw.newLine();
			}
		}

		bw.close();
		fw.close();
	}

	@Override
	public void readDocs(String filename) throws Exception {
		System.out.println("Reading input...");
		try {
			Class.forName("com.mysql.jdbc.Driver"); // 加载MYSQL JDBC驱动程序
	
			System.out.println("Success loading Mysql Driver!");
		} catch (Exception e) {
			System.out.print("Error loading Mysql Driver!");
			e.printStackTrace();
		}

		try {
			Connection connect = DriverManager.getConnection(
					"jdbc:mysql://localhost:3306/rs_train_group", "admin",
					"123");
			// 连接URL为 jdbc:mysql//服务器地址/数据库名 ，后面的2个参数分别是登陆用户名和密码

			System.out.println("Success connect Mysql server!");
			Statement stmt = connect.createStatement();
			String str = "select movieid from movie_content"; 
			ResultSet rs = stmt.executeQuery(str); 
			List<Integer> docList = new ArrayList<Integer>(); 
			while (rs.next()) {

				int docId = Integer.parseInt(rs.getString("movieid"));
				System.out.println(docId);
				docList.add(docId);
			}

			D = docList.size();
			docs = new int[D][];
			docAuthors = new int[D][];
			docsId = docList.toArray(new Integer[0]);
			for (int d = 0; d < docsId.length; d++) {
				int docId = docsId[d];
				System.out.println("Loading doc: " + docId + " Progress: " + d
						+ "/" + docsId.length);
				ResultSet rs2 = stmt
						.executeQuery("select userid from user_movie where movieid="
								+ docId);
				int atCnt = 0;
				while (rs2.next()) {
					atCnt++;
				}
				docAuthors[d] = new int[atCnt];
				for (int na = atCnt - 1; rs2.previous(); na--) {
					int auId = Integer.parseInt(rs2.getString("userid"));
					int key = auMap.size();
					if (!auMap.containsKey(auId)) {
						auMap.put(auId, new Integer(key));
						auMapInv.put(new Integer(key), auId);
					} else {
						key = auMap.get(auId).intValue();
					}
					docAuthors[d][na] = key;
				}
				ResultSet rs3 = stmt
						.executeQuery("select wordid from movie_word where movieid="
								+ docId);

				int wCnt = 0;
				while (rs3.next()) {
					wCnt++;
				}
				docs[d] = new int[wCnt];
				for (int wn = wCnt - 1; rs3.previous(); wn--) {
					int word = Integer.parseInt(rs3.getString("wordid"));
					int key = wordMap.size();
					if (!wordMap.containsKey(word)) {
						wordMap.put(word, new Integer(key));
						wordMapInv.put(new Integer(key), word);
					} else {
						key = ((Integer) wordMap.get(word)).intValue(); 
															
					}
					docs[d][wn] = key;
				}
			}

			// 统计group-users 信息

			Statement stmtg = connect.createStatement();// g
			String strg = "select groupid from household_user ";// g
			ResultSet rsg = stmtg.executeQuery(strg); // g
			List<Integer> groupList = new ArrayList<Integer>();// g

			while (rsg.next()) {

				int groupId = Integer.parseInt(rsg.getString("groupid"));
				System.out.println(groupId);
				groupList.add(groupId);
			}

			G = groupList.size();
			groupUsers = new int[G][];
			groupsId = groupList.toArray(new Integer[0]);// put the array to the 数组 

			for (int g = 0; g < groupsId.length; g++) {
				int groupId = groupsId[g];
				System.out.println("Loading group: " + groupId + " Progress: "
						+ g + "/" + groupsId.length);
				ResultSet rsg2 = stmtg
						.executeQuery("select userid from houserhold_user where groupid="
								+ groupId);
				int usCnt = 0;
				while (rsg2.next()) {
					usCnt++;
				}

				groupUsers[g] = new int[usCnt];
				for (int nu = usCnt - 1; rsg2.previous(); nu--) {
					int usId = Integer.parseInt(rsg2.getString("userid"));
					int key = usMap.size();
					if (!usMap.containsKey(usId)) {
						usMap.put(usId, new Integer(key));
						usMapInv.put(new Integer(key), usId);
					} else {
						key = usMap.get(usId).intValue();
					}
					groupUsers[g][nu] = key;
				}

			}
			// 统计user-movie 信息
			Statement stmtu = connect.createStatement();
			String stru = "select userid from user_movie";
			ResultSet rsu = stmtu.executeQuery(stru);
			List<Integer> userList = new ArrayList<Integer>();
			while (rsu.next()) {
				int userId = Integer.parseInt(rsu.getString("userid"));
				System.out.println(userId);
				userList.add(userId);
			}
			U = userList.size();
			userMovies = new int[U][];
			usersId = userList.toArray(new Integer[0]);
			for (int u = 0; u < usersId.length; u++) {
				int userId = usersId[u];
				System.out.println("Loading user: " + userId + " Progress: "
						+ u + "/" + usersId.length);
				ResultSet rsu2 = stmtu
						.executeQuery("select movieid from user_movie where userid="
								+ userId);
				int mCnt = 0;
				while (rsu2.next()) {
					mCnt++;
				}
				userMovies[u] = new int[mCnt];
				for (int nm = mCnt - 1; rsu2.previous(); nm--) {
					int mId = Integer.parseInt(rsu2.getString("movieid"));
					int key = mMap.size();
					if (!mMap.containsKey(mId)) {
						mMap.put(mId, new Integer(key));
						mMapInv.put(new Integer(key), mId);
					} else {
						key = mMap.get(mId).intValue();
					}
					userMovies[u][nm] = key;
				}

			}

			// user-movie -rating
			umRating = new int[U][D];
			Statement stmtumr = connect.createStatement();
			String strumr = "select all from user_movie";
			ResultSet rsumr = stmtumr.executeQuery(strumr);
			while (rsumr.next()) {
				int userId = Integer.parseInt(rsumr.getString("userid"));
				int movieid = Integer.parseInt(rsumr.getString("movieid"));
				int rating = Integer.parseInt(rsumr.getString("value"));
				int uInv = usMap.get(userId);
				int mInv = mMap.get(movieid);
				umRating[uInv][mInv] = rating;
			}

		} catch (Exception e) {
			System.out.print("get data error!");
			e.printStackTrace();
		}

		W = wordMap.size();
		A = auMap.size();
		U = usMap.size();

		System.out.println(A + " authors");
		System.out.println(D + " documents");
		System.out.println(W + " word types");
		System.out.println(G + "groups");
		System.out.println(U + "users");
	}

	@Override
	public void likelihood() {
		double llh = 0;
		for (int d = 0; d < D; d++) {
			double ad = 1.0 / docAuthors[d].length;
			for (int n = 0; n < docs[d].length; n++) {
				double mAa = 0;
				int w = docs[d][n];
				for (int a = 0; a < docAuthors[d].length; a++) {
					int author = this.docAuthors[d][a];
					for (int z = 0; z < Z; z++) {
						mAa += ((nAZW[author][z][w] + beta) / (nAZ[author][z] + W
								* beta))
								* ((naAZ[author][z] + alpha) / (nA[author] + Z
										* alpha));
						if (mAa * ad > 1) {
							System.out.println("nZWA: " + nAZW[author][z][w]
									+ ", nZA: " + nAZ[author][z]);
							System.out.println(mAa);
						}
					}
				}
				llh += Math.log(ad) + Math.log(mAa);
			}
		}
		// System.out.println("Log-Likelihood: " + llh);
		System.out.println(llh);
	}
}
